#pragma once

#include <boost/any.hpp>
#include <boost/optional.hpp>
#include <boostext/smart_ptr.hpp>
#include <engine.core/math.hpp>

#include "exports.hpp"
#include "Enums.hpp"
#include "Glyph.hpp"
#include "Font.hpp"
#include "WidgetStyleIdentifier.hpp"




namespace Engine
{
	namespace Ui
	{
		class attribute_missing_exception
			: public boostext::exception
		{
		public:

			attribute_missing_exception(const std::string& what)
				: boostext::exception(what)
			{}
		};
		///////////////////////////////////////////////////////////////////////////////////////////





		/**
		 * This class contains every setting for a widget, that can be configured.
		 * Those settings are available for every possible state.
		 *
		 * The only way to create a WidgetStyle is to create it from a css class, created by
		 * a css parser.
		 */
		class ENGINE_UI_API WidgetStyle
		{
		public:

			typedef boost::shared_ptr<WidgetStyle>           ptr;
			typedef boost::weak_ptr<WidgetStyle>             weak_ptr;

			typedef std::map<WidgetState::Type,boost::any>   states;
			typedef std::map<Attributes::Type,states>        AttributeMap;
			typedef std::pair<Attributes::Type,states>       AttributePair;

		private:

			std::vector<WidgetStyle::ptr>   m_default;
			AttributeMap                    m_attributes;

		public:

			WidgetStyle(const ptr& defaultStyle, const AttributeMap& attributes);
			virtual ~WidgetStyle();



			/**
			 * Get the attribute's value with the given or lower state.
			 * Throws when the attribute is not present.
			 * Throws in case of a type-mismatch.
			 */
			template <typename T>
			const T& attribute(Attributes::Type attr, WidgetState::Type state = WidgetState::Normal)
			{
				const T* t = boost::any_cast<T>(any_attribute(attr, state));
				if(!t)
					BOOSTEXT_THROW(boostext::bad_cast(boostext::format("The attribute '%1%' cannot be extracted") % attr));

				return *t;
			}

			/**
			 * Get the attribute's value with the given or lower state.
			 * Returns the default value when the attribute is not present.
			 * Throws in case of a type-mismatch.
			 */
			template <typename T>
			const T& attribute(Attributes::Type attr, WidgetState::Type state, const T& defaultValue)
			{
				if(hasAttribute(attr, state))
				{
					const T* t = boost::any_cast<T>(any_attribute(attr, state));
					if(!t)
						BOOSTEXT_THROW(boostext::bad_cast(boostext::format("The attribute '%1%' cannot be extracted") % attr));

					return *t;
				}
				else
				{
					return defaultValue;
				}
			}

			/**
			 * Test if a certain attribute is present.
			 */
			bool hasAttribute(const Attributes::Type& attr, const WidgetState::Type& state = WidgetState::Normal) const;

			/**
			 * Get the border type that corresponds to the given attribute type.
			 */
			static Border::Type borderFromAttribute(Attributes::Type attribute);

		private:

			const boost::any* any_attribute(const Attributes::Type& attr, WidgetState::Type state) const;
			const boost::any* any_attribute_default(const Attributes::Type& attr, WidgetState::Type state) const;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
